﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace Ndo.FM.CodeGen
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        static string com(int count)
        {
            return count > 0 ? ", " : "";
        }
        static string p0(int count)
        {
            return string.Format("Parameter{0}, ", count);
        }
        static string p1(int count)
        {
            return string.Format("p{0}, ", count);
        }
        static string p(int count)
        {
            return string.Format("P{0}, ", count);
        }
        static string pp(int count)
        {
            return string.Format("P{0} p{0}, ", count);
        }
        static List<string> plist = new List<string>();
        static string ps(int count)
        {
            while (plist.Count <= count)
                plist.Add(null);
            if (plist[count] != null)
                return plist[count];
            StringBuilder sb = new StringBuilder();
            for (int i = 1; i <= count; i++)
                sb.Append(p(i));
            if (count > 0)
                sb.Length -= 2;
            return plist[count] = sb.ToString();
        }
        static List<string> plist0 = new List<string>();
        static string ps0(int count)
        {
            while (plist0.Count <= count)
                plist0.Add(null);
            if (plist0[count] != null)
                return plist0[count];
            StringBuilder sb = new StringBuilder();
            for (int i = 1; i <= count; i++)
                sb.Append(p0(i));
            if (count > 0)
                sb.Length -= 2;
            return plist0[count] = sb.ToString();
        }
        static List<string> plist1 = new List<string>();
        static string ps1(int count)
        {
            while (plist1.Count <= count)
                plist1.Add(null);
            if (plist1[count] != null)
                return plist1[count];
            StringBuilder sb = new StringBuilder();
            for (int i = 1; i <= count; i++)
                sb.Append(p1(i));
            if (count > 0)
                sb.Length -= 2;
            return plist1[count] = sb.ToString();
        }
        static List<string> pplist = new List<string>();
        static string pps(int count)
        {
            while (pplist.Count <= count)
                pplist.Add(null);
            if (pplist[count] != null)
                return pplist[count];
            StringBuilder sb = new StringBuilder();
            for (int i = 1; i <= count; i++)
                sb.Append(pp(i));
            if (count > 0)
                sb.Length -= 2;
            return pplist[count] = sb.ToString();
        }
        static void F(StringBuilder sb, int count)
        {
            for (int i = 0; i <= count; i++)
            {
                sb.AppendFormat("\tpublic delegate R Function<R{0}{1}>({2});\r\n", com(i), ps(i), pps(i));
            }
        }

        static void M(StringBuilder sb, int count)
        {
            for (int i = 0; i <= count; i++)
            {
                sb.Append("\tpublic delegate void Method");
                if (i > 0)
                {
                    sb.AppendFormat("<{0}>", ps(i));
                }
                sb.AppendFormat("({0});\r\n", pps(i));
            }
        }

        static void AF(StringBuilder sb, int count)
        {
            StringBuilder sb0 = new StringBuilder();
            for (int i = 1; i <= count; i++)
            {
                sb.AppendFormat("\tpublic class AFunction<R, {0}>\r\n\t{{\r\n", ps(i));
                sb.AppendFormat("\t\tpublic readonly Function<R, {0}> F;\r\n", ps(i));
                for(int j=1; j<=i; j++)
                    sb.AppendFormat("\t\tpublic P{0} Parameter{0};\r\n", j);
                sb.Append("\t\tpublic AFunction(Delegate f)\r\n\t\t{\r\n");
                sb.AppendFormat("\t\t\tF = (Function<R, {0}>)Delegate.CreateDelegate(typeof(Function<R, {0}>), f.Target, f.Method);\r\n", ps(i));
                sb.Append("\t\t}\r\n");
                sb.AppendFormat("\t\tpublic AFunction(Delegate f, {0})\r\n\t\t{{\r\n", pps(i));
                sb.AppendFormat("\t\t\tF = (Function<R, {0}>)Delegate.CreateDelegate(typeof(Function<R, {0}>), f.Target, f.Method);\r\n", ps(i));
                for(int j=1; j<=i; j++)
                    sb.AppendFormat("\t\t\tParameter{0} = p{0};\r\n", j);
                sb.Append("\t\t}\r\n");

                sb.AppendFormat("\t\tpublic R Call()\r\n\t\t{{\r\n\t\t\treturn F({0});\r\n\t\t}}\r\n", ps0(i));
                sb.AppendFormat("\t\tpublic static Function<R> From(Delegate f, {0})\r\n\t\t{{\r\n\t\t\treturn new AFunction<R, {1}>(f, {2}).Call;\r\n\t\t}}\r\n", pps(i), ps(i), ps1(i));

                for(int j=1; j<=i; j++)
                    for(int k=1;k+j<=i+1; k++)
                        if(k!=i)
                        {
                            sb.Append("\t\tpublic R Call");
                            sb.Append(j);
                            sb.Append("(");
                            for (int x = j; x < k + j; x++)
                                sb.Append(pp(x));
                            sb.Length -= 2;
                            sb.Append(")\r\n\t\t{\r\n\t\t\treturn F(");
                            for (int x = 1; x < j; x++)
                                sb.AppendFormat("Parameter{0}, ", x);
                            for (int x = j; x < j + k; x++)
                                sb.AppendFormat("p{0}, ", x);
                            for (int x = j + k; x <= i; x++)
                                sb.AppendFormat("Parameter{0}, ", x);
                            sb.Length -= 2;
                            sb.Append(");\r\n\t\t}\r\n");

                            sb0.Length = 0;
                            sb0.Append("\t\tpublic static Function<R, ");
                            for (int x = j; x < j + k; x++)
                                sb0.Append(p(x));
                            sb0.Length -= 2;
                            sb0.Append("> At");
                            sb0.Append(j);
                            sb0.Append("(");
                            for (int x = 1; x < j; x++)
                                sb0.Append(pp(x));
                            sb0.Append("Delegate f, ");
                            for (int x = j + k; x <= i; x++)
                                sb0.Append(pp(x));
                            sb0.Length -= 2;
                            sb0.AppendFormat(")\r\n\t\t{{\r\n\t\t\treturn new AFunction<R, {0}>(f, ", ps(i));
                            for (int x = 1; x < j; x++)
                                sb0.AppendFormat("p{0}, ", x);
                            for (int x = j; x < j + k; x++)
                                sb0.AppendFormat("default(P{0}), ", x);
                            for (int x = j + k; x <= i; x++)
                                sb0.AppendFormat("p{0}, ", x);
                            sb0.Length -= 2;
                            sb0.AppendFormat(").Call{0};\r\n\t\t}}\r\n", j);

                            sb.Append(sb0.ToString());

                            if(j==1)
                            {
                                sb0.Replace(" At1(", " AtBegin(");
                                sb.Append(sb0.ToString());
                            }
                            else if (j + k > i)
                            {
                                sb0.Replace(" At" + j + "(", " AtEnd(");
                                sb.Append(sb0.ToString());
                            }
                            

                        }
                sb.Append("\t}\r\n");
            }
        }

        public static void AM(StringBuilder sb, int count)
        {
            StringBuilder sb1 = new StringBuilder();
            AF(sb1, count);
            sb1.Replace("<R, ", "<");
            sb1.Replace("Function<R>", "Method");
            sb1.Replace("Function", "Method");
            sb1.Replace(" R Call", " void Call");
            sb1.Replace("return F(", "F(");
            sb.Append(sb1.ToString());
        }

        private void button1_Click(object sender, EventArgs e)
        {
            int n = 16;
            System.IO.StreamWriter sw = new StreamWriter("function.cs");
            StringBuilder sb = new StringBuilder();
            sb.Append("using System;\r\n\r\nnamespace Ndo.FM\r\n{\r\n");
            F(sb, n);
            AF(sb, n);
            sb.Append("}\r\n");
            sw.Write(sb.ToString());
            sw.Close();
            sb.Length = 0;
            sw = new StreamWriter("method.cs");
            sb.Append("using System;\r\n\r\nnamespace Ndo.FM\r\n{\r\n");
            M(sb, n);
            AM(sb, n);
            sb.Append("}\r\n");
            sw.Write(sb.ToString());
            sw.Close();
        }
    }
}